﻿namespace TKageyu.Utils
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Text;

    public class IconExtractor : IDisposable
    {
        private string _filename = null;
        private IntPtr _hModule = IntPtr.Zero;
        private Icon[] _iconCache = null;
        private IconResInfo _resInfo = null;
        private const int ERROR_BAD_EXE_FORMAT = 0xc1;
        private const int ERROR_FILE_NOT_FOUND = 2;
        private const int ERROR_RESOURCE_TYPE_NOT_FOUND = 0x715;
        private const int ERROR_SUCCESS = 0;
        private const int LOAD_LIBRARY_AS_DATAFILE = 2;
        private const int MAX_PATH = 260;
        private const int RT_GROUP_ICON = 14;
        private const int RT_ICON = 3;
        private const int sGRPICONDIRENTRY = 14;
        private const int sICONDIR = 6;
        private const int sICONDIRENTRY = 0x10;

        public IconExtractor(string filename)
        {
            if (filename == null)
            {
                throw new ArgumentNullException("filename");
            }
            this._hModule = LoadLibrary(filename);
            if (this._hModule == IntPtr.Zero)
            {
                this._hModule = LoadLibraryEx(filename, IntPtr.Zero, 2);
                if (this._hModule == IntPtr.Zero)
                {
                    switch (Marshal.GetLastWin32Error())
                    {
                        case 2:
                            throw new FileNotFoundException("Specified file '" + filename + "' not found.");

                        case 0xc1:
                            throw new ArgumentException("Specified file '" + filename + "' is not an executable file or DLL.");
                    }
                    throw new Win32Exception();
                }
            }
            StringBuilder lpFilename = new StringBuilder(260);
            if (GetModuleFileName(this._hModule, lpFilename, lpFilename.Capacity + 1) != 0)
            {
                this._filename = lpFilename.ToString();
            }
            else
            {
                if (Marshal.GetLastWin32Error() != 0)
                {
                    throw new Win32Exception();
                }
                this._filename = filename;
            }
            this._resInfo = new IconResInfo();
            if (!EnumResourceNames(this._hModule, 14, new EnumResNameProc(this.EnumResNameCallBack), this._resInfo))
            {
                throw new Win32Exception();
            }
            this._iconCache = new Icon[this.IconCount];
        }

        private Icon CreateIcon(int iconIndex)
        {
            Icon icon;
            byte[] buffer = this.GetResourceData(this._hModule, this._resInfo.IconNames[iconIndex], 14);
            using (MemoryStream stream = new MemoryStream())
            {
                using (BinaryWriter writer = new BinaryWriter(stream))
                {
                    int num = BitConverter.ToUInt16(buffer, 4);
                    int num2 = 6 + (0x10 * num);
                    writer.Write(buffer, 0, 6);
                    for (int i = 0; i < num; i++)
                    {
                        writer.BaseStream.Seek((long) (6 + (0x10 * i)), SeekOrigin.Begin);
                        writer.Write(buffer, 6 + (14 * i), 12);
                        writer.Write(num2);
                        IntPtr lpName = (IntPtr) BitConverter.ToUInt16(buffer, (6 + (14 * i)) + 12);
                        byte[] buffer2 = this.GetResourceData(this._hModule, lpName, 3);
                        writer.BaseStream.Seek((long) num2, SeekOrigin.Begin);
                        writer.Write(buffer2, 0, buffer2.Length);
                        num2 += buffer2.Length;
                    }
                    stream.Seek(0L, SeekOrigin.Begin);
                    icon = new Icon(stream);
                }
            }
            return icon;
        }

        public void Dispose()
        {
            if (this._hModule != IntPtr.Zero)
            {
                try
                {
                    FreeLibrary(this._hModule);
                }
                catch
                {
                }
                this._hModule = IntPtr.Zero;
            }
            if (this._iconCache != null)
            {
                foreach (Icon icon in this._iconCache)
                {
                    if (icon != null)
                    {
                        try
                        {
                            icon.Dispose();
                        }
                        catch
                        {
                        }
                    }
                }
                this._iconCache = null;
            }
        }

        private bool EnumResNameCallBack(IntPtr hModule, int lpszType, IntPtr lpszName, IconResInfo lParam)
        {
            if (lpszType == 14)
            {
                lParam.IconNames.Add(new ResourceName(lpszName));
            }
            return true;
        }

        [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern bool EnumResourceNames(IntPtr hModule, int lpszType, EnumResNameProc lpEnumFunc, IconResInfo lParam);
        ~IconExtractor()
        {
            this.Dispose();
        }

        [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, int lpType);
        [DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
        private static extern bool FreeLibrary(IntPtr hModule);
        public Icon GetIcon(int iconIndex)
        {
            if (this._hModule == IntPtr.Zero)
            {
                throw new ObjectDisposedException("IconExtractor");
            }
            if ((iconIndex < 0) || (this.IconCount <= iconIndex))
            {
                int num = this.IconCount - 1;
                throw new ArgumentException("iconIndex is out of range. It should be between 0 and " + num.ToString() + ".");
            }
            if (this._iconCache[iconIndex] == null)
            {
                this._iconCache[iconIndex] = this.CreateIcon(iconIndex);
            }
            return (Icon) this._iconCache[iconIndex].Clone();
        }

        [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern int GetModuleFileName(IntPtr hModule, StringBuilder lpFilename, int nSize);
        private byte[] GetResourceData(IntPtr hModule, IntPtr lpName, int lpType)
        {
            IntPtr hResInfo = FindResource(hModule, lpName, lpType);
            if (hResInfo == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            IntPtr hResData = LoadResource(hModule, hResInfo);
            if (hResData == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            IntPtr source = LockResource(hResData);
            if (source == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            int num = SizeofResource(hModule, hResInfo);
            if (num == 0)
            {
                throw new Win32Exception();
            }
            byte[] destination = new byte[num];
            Marshal.Copy(source, destination, 0, destination.Length);
            return destination;
        }

        private byte[] GetResourceData(IntPtr hModule, ResourceName name, int lpType)
        {
            byte[] buffer;
            try
            {
                IntPtr lpName = name.GetValue();
                buffer = this.GetResourceData(hModule, lpName, lpType);
            }
            finally
            {
                name.Free();
            }
            return buffer;
        }

        [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern IntPtr LoadLibrary(string lpFileName);
        [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);
        [DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
        private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
        [DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
        private static extern IntPtr LockResource(IntPtr hResData);
        [DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
        private static extern int SizeofResource(IntPtr hModule, IntPtr hResInfo);
        public static Icon[] SplitIcon(Icon icon)
        {
            if (icon == null)
            {
                throw new ArgumentNullException("icon");
            }
            byte[] buffer = null;
            using (MemoryStream stream = new MemoryStream())
            {
                icon.Save(stream);
                buffer = stream.ToArray();
            }
            List<Icon> list = new List<Icon>();
            int num = BitConverter.ToInt16(buffer, 4);
            for (int i = 0; i < num; i++)
            {
                using (MemoryStream stream2 = new MemoryStream())
                {
                    using (BinaryWriter writer = new BinaryWriter(stream2))
                    {
                        writer.Write(buffer, 0, 4);
                        writer.Write((short) 1);
                        writer.Write(buffer, 6 + (0x10 * i), 12);
                        writer.Write(0x16);
                        int count = BitConverter.ToInt32(buffer, (6 + (0x10 * i)) + 8);
                        int index = BitConverter.ToInt32(buffer, (6 + (0x10 * i)) + 12);
                        writer.Write(buffer, index, count);
                        stream2.Seek(0L, SeekOrigin.Begin);
                        list.Add(new Icon(stream2));
                    }
                }
            }
            return list.ToArray();
        }

        public override string ToString()
        {
            return string.Format("IconExtractor (Filename: '{0}', IconCount: {1})", this.Filename, this.IconCount);
        }

        public string Filename
        {
            get
            {
                return this._filename;
            }
        }

        public int IconCount
        {
            get
            {
                return this._resInfo.IconNames.Count;
            }
        }

        [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet=CharSet.Auto)]
        private delegate bool EnumResNameProc(IntPtr hModule, int lpszType, IntPtr lpszName, IconExtractor.IconResInfo lParam);

        private class IconResInfo
        {
            public List<IconExtractor.ResourceName> IconNames = new List<IconExtractor.ResourceName>();
        }

        private class ResourceName
        {
            private IntPtr _bufPtr = IntPtr.Zero;
            [CompilerGenerated]
            private IntPtr _Id_k__BackingField;
            [CompilerGenerated]
            private string _Name_k__BackingField;

            public ResourceName(IntPtr lpName)
            {
                if ((((int) lpName) >> 0x10) == 0)
                {
                    this.Id = lpName;
                    this.Name = null;
                }
                else
                {
                    this.Id = IntPtr.Zero;
                    this.Name = Marshal.PtrToStringAuto(lpName);
                }
            }

            public void Free()
            {
                if (this._bufPtr != IntPtr.Zero)
                {
                    try
                    {
                        Marshal.FreeHGlobal(this._bufPtr);
                    }
                    catch
                    {
                    }
                    this._bufPtr = IntPtr.Zero;
                }
            }

            public IntPtr GetValue()
            {
                if (this.Name == null)
                {
                    return this.Id;
                }
                this._bufPtr = Marshal.StringToHGlobalAuto(this.Name);
                return this._bufPtr;
            }

            public IntPtr Id
            {
                [CompilerGenerated]
                get
                {
                    return this._Id_k__BackingField;
                }
                [CompilerGenerated]
                private set
                {
                    this._Id_k__BackingField = value;
                }
            }

            public string Name
            {
                [CompilerGenerated]
                get
                {
                    return this._Name_k__BackingField;
                }
                [CompilerGenerated]
                private set
                {
                    this._Name_k__BackingField = value;
                }
            }
        }
    }
}

